<!DOCTYPE HTML>
<html lang="zh-Hans" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>译：Rust生命周期常见误区 - Rust 优秀博文</title>


        <!-- Custom HTML head -->
        
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../../favicon.svg">
        <link rel="shortcut icon" href="../../favicon.png">
        <link rel="stylesheet" href="../../css/variables.css">
        <link rel="stylesheet" href="../../css/general.css">
        <link rel="stylesheet" href="../../css/chrome.css">
        <link rel="stylesheet" href="../../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../../highlight.css">
        <link rel="stylesheet" href="../../tomorrow-night.css">
        <link rel="stylesheet" href="../../ayu-highlight.css">

        <!-- Custom theme stylesheets -->

    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">1.</strong> 类型系统</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../01_类型系统/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow.html"><strong aria-hidden="true">1.1.</strong> Rust Concept Clarification Deref vs AsRef vs Borrow vs Cow</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow/Deref_AsRef_Borrow_Cow释义.html"><strong aria-hidden="true">1.2.</strong> Deref AsRef Borrow Cow 释义</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然/Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然.html"><strong aria-hidden="true">1.3.</strong> Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust的Cow类型有什么用？详解Cow及其用途/Rust的Cow类型有什么用？详解Cow及其用途.html"><strong aria-hidden="true">1.4.</strong> Rust的Cow类型有什么用？详解Cow及其用途</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/判别Fn、FnMut、FnOnce的标准/判别Fn、FnMut、FnOnce的标准.html"><strong aria-hidden="true">1.5.</strong> 判别Fn、FnMut、FnOnce的标准</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/一行代码告诉你内部可变性的真相(UnsafeCell)/一行代码告诉你内部可变性的真相(UnsafeCell).html"><strong aria-hidden="true">1.6.</strong> 一行代码告诉你内部可变性的真相(UnsafeCell)</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Tour_of_Rust's_Standard_Library_Traits/Tour_of_Rust's_Standard_Library_Traits.html"><strong aria-hidden="true">1.7.</strong> Tour of Rust's Standard Library Traits</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/逆变、协变与子类型，以及Rust/逆变、协变与子类型，以及Rust.html"><strong aria-hidden="true">1.8.</strong> 逆变、协变与子类型，以及Rust</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust自引用结构、Pin与Unpin/Rust自引用结构、Pin与Unpin.html"><strong aria-hidden="true">1.9.</strong> Rust自引用结构、Pin与Unpin</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/为什么Rust需要Pin,Unpin/为什么Rust需要Pin,Unpin.html"><strong aria-hidden="true">1.10.</strong> 译：为什么 Rust 需要 Pin, Unpin ？</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/定海神针Pin和Unpin/定海神针Pin和Unpin.html"><strong aria-hidden="true">1.11.</strong> 译：定海神针 Pin 和 Unpin</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/sizedness-in-rust/sizedness-in-rust.html"><strong aria-hidden="true">1.12.</strong> Sizedness in Rust</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust生命周期集大成者PhantomData〈T〉/Rust生命周期集大成者PhantomData〈T〉.html"><strong aria-hidden="true">1.13.</strong> Rust生命周期集大成者 PhantomData&lt;T&gt;</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用Rust做类型体操_Part_0.html"><strong aria-hidden="true">1.14.</strong> 数据库表达式执行的黑魔法：用Rust做类型体操 Part 0</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：GAT实现引用类型关联_Part_1.html"><strong aria-hidden="true">1.15.</strong> 数据库表达式执行的黑魔法：GAT实现引用类型关联 Part 1</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用HRTB写bound_Part_2.html"><strong aria-hidden="true">1.16.</strong> 数据库表达式执行的黑魔法：用HRTB写bound Part 2</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用Rust做类型体操之用宏展开重复代码_Part_3_&_4.html"><strong aria-hidden="true">1.17.</strong> 数据库表达式执行的黑魔法：用Rust做类型体操之用宏展开重复代码 Part 3 & 4</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：与Rust编译器斗智斗勇之表达式向量化_Part_5_&_6.html"><strong aria-hidden="true">1.18.</strong> 数据库表达式执行的黑魔法：与Rust编译器斗智斗勇之表达式向量化 Part 5 & 6</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：在Rust中用宏关联逻辑类型和实际类型_Part_7.html"><strong aria-hidden="true">1.19.</strong> 数据库表达式执行的黑魔法：在Rust中用宏关联逻辑类型和实际类型 Part 7</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">2.</strong> 生命周期</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../02_生命周期/Rust中的生命周期——从StrSplit实例说开去/Rust中的生命周期——从StrSplit实例说开去.html"><strong aria-hidden="true">2.1.</strong> Rust中的生命周期——从StrSplit实例说开去</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/与ChatGPT深度对话来学Rust生命周期/与ChatGPT深度对话来学Rust生命周期.html"><strong aria-hidden="true">2.2.</strong> 与ChatGPT深度对话来学Rust生命周期</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——early_bound与late_bound（1）/进击的Rust生命周期——early_bound与late_bound（1）.html"><strong aria-hidden="true">2.3.</strong> 进击的Rust生命周期——early_bound与late_bound（1）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——early_bound与late_bound（2）/进击的Rust生命周期——early_bound与late_bound（2）.html"><strong aria-hidden="true">2.4.</strong> 进击的Rust生命周期——early_bound与late_bound（2）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——一力降十会的MIR（1）/进击的Rust生命周期——一力降十会的MIR（1）.html"><strong aria-hidden="true">2.5.</strong> 进击的Rust生命周期——一力降十会的MIR（1）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——一力降十会的MIR（2）/进击的Rust生命周期——一力降十会的MIR（2）.html"><strong aria-hidden="true">2.6.</strong> 进击的Rust生命周期——一力降十会的MIR（2）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/Common_Rust_Lifetime_Misconceptions/Common_Rust_Lifetime_Misconceptions.html"><strong aria-hidden="true">2.7.</strong> Common Rust Lifetime Misconceptions</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/Rust生命周期常见误区/Rust生命周期常见误区.html" class="active"><strong aria-hidden="true">2.8.</strong> 译：Rust生命周期常见误区</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">3.</strong> 无畏并发</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../05_无畏并发/简单写个Rust无锁队列/简单写个Rust无锁队列.html"><strong aria-hidden="true">3.1.</strong> 简单写个Rust无锁队列</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——混合自旋锁/进击的Rust多线程——混合自旋锁.html"><strong aria-hidden="true">3.2.</strong> 进击的Rust多线程——混合自旋锁</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/An_unsafe_tour_of_Rust's_Send_and_Sync/An_unsafe_tour_of_Rust's_Send_and_Sync.html"><strong aria-hidden="true">3.3.</strong> An unsafe tour of Rust's Send and Sync</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——Send与Sync/进击的Rust多线程——Send与Sync.html"><strong aria-hidden="true">3.4.</strong> 进击的Rust多线程——Send与Sync</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——离经叛道的PhantomData/进击的Rust多线程——离经叛道的PhantomData.html"><strong aria-hidden="true">3.5.</strong> 进击的Rust多线程——离经叛道的PhantomData</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust_Async_Pin概念解析/Rust_Async_Pin概念解析.html"><strong aria-hidden="true">3.6.</strong> Rust Async: Pin概念解析</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust和C++的并发库对比/Rust和C++的并发库对比.html"><strong aria-hidden="true">3.7.</strong> 译：Rust 和 C++ 的并发库对比</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust原子类型和内存排序/Rust原子类型和内存排序.html"><strong aria-hidden="true">3.8.</strong> Rust原子类型和内存排序</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">4.</strong> 网络编程</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../06_网络编程/从编解码层面理解WebSocket_手写一个WebSocket/从编解码层面理解WebSocket_手写一个WebSocket.html"><strong aria-hidden="true">4.1.</strong> 从编解码层面理解WebSocket 手写一 个WebSocket</a></li><li class="chapter-item expanded "><a href="../../06_网络编程/透过Rust探索系统的本原：网络篇/透过Rust探索系统的本原：网络篇.html"><strong aria-hidden="true">4.2.</strong> 透过Rust探索系统的本原：网络篇</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">5.</strong> 轮子系列</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../07_轮子系列/700行Rust写一个内存分配器/700行Rust写一个内存分配器.html"><strong aria-hidden="true">5.1.</strong> 700行Rust写一个内存分配器</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/Rust：网络库的实现思路/Rust：网络库的实现思路.html"><strong aria-hidden="true">5.2.</strong> Rust：网络库的实现思路</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/Rust异步运行时基础部件/Rust异步运行时基础部件.html"><strong aria-hidden="true">5.3.</strong> Rust异步运行时基础部件</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（1）.html"><strong aria-hidden="true">5.4.</strong> 使用Rust+epoll编写异步IO框架（1）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（2）.html"><strong aria-hidden="true">5.5.</strong> 使用Rust+epoll编写异步IO框架（2）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（3）.html"><strong aria-hidden="true">5.6.</strong> 使用Rust+epoll编写异步IO框架（3）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day1.html"><strong aria-hidden="true">5.7.</strong> 用rust从零开发一套web框架：day1</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day2.html"><strong aria-hidden="true">5.8.</strong> 用rust从零开发一套web框架：day2</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day3.html"><strong aria-hidden="true">5.9.</strong> 用rust从零开发一套web框架：day3</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day4.html"><strong aria-hidden="true">5.10.</strong> 用rust从零开发一套web框架：day4</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day5.html"><strong aria-hidden="true">5.11.</strong> 用rust从零开发一套web框架：day5</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">6.</strong> 奇技淫巧</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Copy-On-Write是不是优化？/Copy-On-Write是不是优化？.html"><strong aria-hidden="true">6.1.</strong> 译：Copy-On-Write是不是优化？</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/揭秘神奇的Rust_Axum风格的函数实现/揭秘神奇的Rust_Axum风格的函数实现.html"><strong aria-hidden="true">6.2.</strong> 译：揭秘神奇的 Rust Axum 风格的函数实现</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/“变长参数”函数与回调/“变长参数”函数与回调.html"><strong aria-hidden="true">6.3.</strong> “变长参数”函数与回调</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Rust字符串格式化的幕后：format_args!()/Rust字符串格式化的幕后：format_args!().html"><strong aria-hidden="true">6.4.</strong> 译：Rust字符串格式化的幕后：format_args!()</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/给Rust带来一点C++特产/给Rust带来一点C++特产.html"><strong aria-hidden="true">6.5.</strong> 给Rust带来一点C++特产</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/一步步实现_Rust_Bevy_ECS_的_System_简化版本/一步步实现_Rust_Bevy_ECS_的_System_简化版本.html"><strong aria-hidden="true">6.6.</strong> 一步步实现 Rust Bevy ECS 的 System 简化版本</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Exploring_Design_Patterns_in_Rust_with_Algorithmic_Trading_Examples/Exploring_Design_Patterns_in_Rust_with_Algorithmic_Trading_Examples.html"><strong aria-hidden="true">6.7.</strong> Exploring Design Patterns in Rust with Algorithmic Trading Examples</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">7.</strong> 源码分析</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：bytes源码分析/Rust并发：bytes源码分析.html"><strong aria-hidden="true">7.1.</strong> Rust并发：bytes源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：标准库Arc源码分析/Rust并发：标准库Arc源码分析.html"><strong aria-hidden="true">7.2.</strong> Rust并发：标准库Arc源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：标准库sync_Once源码分析/Rust并发：标准库sync_Once源码分析.html"><strong aria-hidden="true">7.3.</strong> Rust并发：标准库sync::Once源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust源码阅读：引用计数Rc/Rust源码阅读：引用计数Rc.html"><strong aria-hidden="true">7.4.</strong> Rust源码阅读：引用计数Rc</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust源码阅读：Cell、RefCell与内部可变性/Rust源码阅读：Cell、RefCell与内部可变性.html"><strong aria-hidden="true">7.5.</strong> Rust源码阅读： Cell、RefCell与内部可变性</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/关于_Rust_的_UnsafeCell、Cell_与_RefCell/关于_Rust_的_UnsafeCell、Cell_与_RefCell.html"><strong aria-hidden="true">7.6.</strong> 关于 Rust 的 UnsafeCell、Cell 与 RefCell</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust_Async_async-stream源码分析/Rust_Async_async-stream源码分析.html"><strong aria-hidden="true">7.7.</strong> Rust Async: async-stream源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/走进Tokio的异步世界/走进Tokio的异步世界.html"><strong aria-hidden="true">7.8.</strong> 走进 Tokio 的异步世界</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/tokio.rs_runtime的实现/tokio.rs_runtime的实现.html"><strong aria-hidden="true">7.9.</strong> tokio.rs runtime 的实现</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_internals/Tokio_internals.html"><strong aria-hidden="true">7.10.</strong> Tokio internals</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_internals/译文：Tokio內部机制.html"><strong aria-hidden="true">7.11.</strong> 译：Tokio 内部机制</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust_Axum_HTTP_框架的架构分析/Rust_Axum_HTTP_框架的架构分析.html"><strong aria-hidden="true">7.12.</strong> Rust Axum HTTP 框架的架构分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/安利一个Rust_Game_Engine：Bevy--ECS部分/安利一个Rust_Game_Engine：Bevy--ECS部分.html"><strong aria-hidden="true">7.13.</strong> 安利一个Rust Game Engine：Bevy--ECS部分</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_解析之任务调度/Tokio_解析之任务调度.html"><strong aria-hidden="true">7.14.</strong> Tokio 解析之任务调度</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">8.</strong> 生态观察</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../11_生态观察/Rust_web_frameworks_have_subpar_error_reporting/Rust_web_frameworks_have_subpar_error_reporting.html"><strong aria-hidden="true">8.1.</strong> Rust web frameworks have subpar error reporting</a></li><li class="chapter-item expanded "><a href="../../11_生态观察/SeaORM：要做Rust版本的ActiveRecord/SeaORM：要做Rust版本的ActiveRecord.html"><strong aria-hidden="true">8.2.</strong> SeaORM：要做Rust版本的ActiveRecord</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">9.</strong> 死灵终极</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_总述.html"><strong aria-hidden="true">9.1.</strong> 译：Learn Rust the Dangerous Way 总述</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_0.html"><strong aria-hidden="true">9.2.</strong> 译：Learn Rust the Dangerous Way 0</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_1.html"><strong aria-hidden="true">9.3.</strong> 译：Learn Rust the Dangerous Way 1</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_2.html"><strong aria-hidden="true">9.4.</strong> 译：Learn Rust the Dangerous Way 2</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_3.html"><strong aria-hidden="true">9.5.</strong> 译：Learn Rust the Dangerous Way 3</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_4.html"><strong aria-hidden="true">9.6.</strong> 译：Learn Rust the Dangerous Way 4</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_5.html"><strong aria-hidden="true">9.7.</strong> 译：Learn Rust the Dangerous Way 5</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（一）.html"><strong aria-hidden="true">9.8.</strong> Unsafe Rust 随堂小测（一）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（二）.html"><strong aria-hidden="true">9.9.</strong> Unsafe Rust 随堂小测（二）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（三）.html"><strong aria-hidden="true">9.10.</strong> Unsafe Rust 随堂小测（三）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测参考答案.html"><strong aria-hidden="true">9.11.</strong> Unsafe Rust 随堂小测参考答案</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Rust 优秀博文</h1>

                    <div class="right-buttons">
                        <a href="../../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="rust生命周期常见误区"><a class="header" href="#rust生命周期常见误区">Rust生命周期常见误区</a></h1>
<p>作者：<a href="https://github.com/pretzelhammer">https://github.com/pretzelhammer</a></p>
<p>原载：<a href="https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md">https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md</a></p>
<p>译者：<a href="https://github.com/whfuyn/rust-blog/commits?author=whfuyn">whfuyn</a></p>
<p>译文：<a href="https://github.com/whfuyn/rust-blog/blob/master/posts/Rust%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E7%9A%84%E5%B8%B8%E8%A7%81%E8%AF%AF%E8%A7%A3.md">https://github.com/whfuyn/rust-blog/blob/master/posts/Rust%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E7%9A%84%E5%B8%B8%E8%A7%81%E8%AF%AF%E8%A7%A3.md</a></p>
<p>5月19日, 2020 · 阅读大概需要37分钟 · #rust · #生命周期</p>
<p><strong>目录</strong></p>
<ul>
<li><a href="#%E4%BB%8B%E7%BB%8D">介绍</a></li>
<li><a href="#%E8%AF%AF%E8%A7%A3%E5%88%97%E8%A1%A8">误解列表</a>
<ul>
<li><a href="#1-T-%E5%8F%AA%E5%8C%85%E5%90%AB%E6%89%80%E6%9C%89%E6%9D%83%E7%B1%BB%E5%9E%8B">1) <code>T</code> 只包含所有权类型</a></li>
<li><a href="#2-%E5%A6%82%E6%9E%9C-T-static-%E9%82%A3%E4%B9%88-T-%E5%BF%85%E9%A1%BB%E5%9C%A8%E6%95%B4%E4%B8%AA%E7%A8%8B%E5%BA%8F%E8%BF%90%E8%A1%8C%E4%B8%AD%E9%83%BD%E6%98%AF%E6%9C%89%E6%95%88%E7%9A%84">2) 如果 <code>T: 'static</code> 那么 <code>T</code> 必须在整个程序运行中都是有效的</a></li>
<li><a href="#3-a-T-%E5%92%8C-T-a-%E6%98%AF%E7%9B%B8%E5%90%8C%E7%9A%84">3) <code>&amp;'a T</code> 和 <code>T: 'a</code> 是相同的</a></li>
<li><a href="#4-%E6%88%91%E7%9A%84%E4%BB%A3%E7%A0%81%E6%B2%A1%E7%94%A8%E5%88%B0%E6%B3%9B%E5%9E%8B%EF%BC%8C%E4%B9%9F%E4%B8%8D%E5%90%AB%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F">4) 我的代码没用到泛型，也不含生命周期</a></li>
<li><a href="#5-%E5%A6%82%E6%9E%9C%E7%BC%96%E8%AF%91%E8%83%BD%E9%80%9A%E8%BF%87%EF%BC%8C%E9%82%A3%E4%B9%88%E6%88%91%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E6%A0%87%E6%B3%A8%E5%B0%B1%E6%98%AF%E6%AD%A3%E7%A1%AE%E7%9A%84">5) 如果编译能通过，那么我的生命周期标注就是正确的</a></li>
<li><a href="#6-%E8%A3%85%E7%AE%B1%E7%9A%84trait%E5%AF%B9%E8%B1%A1%E6%B2%A1%E6%9C%89%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F">6) 装箱的trait对象没有生命周期</a></li>
<li><a href="#7-%E7%BC%96%E8%AF%91%E5%99%A8%E6%8A%A5%E9%94%99%E4%BF%A1%E6%81%AF%E4%BC%9A%E5%91%8A%E8%AF%89%E6%88%91%E6%80%8E%E4%B9%88%E4%BF%AE%E6%94%B9%E6%88%91%E7%9A%84%E4%BB%A3%E7%A0%81">7) 编译器报错信息会告诉我怎么修改我的代码</a></li>
<li><a href="#8-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%8F%AF%E4%BB%A5%E5%9C%A8%E8%BF%90%E8%A1%8C%E6%97%B6%E5%8F%98%E9%95%BF%E7%BC%A9%E7%9F%AD">8) 生命周期可以在运行时变长缩短</a></li>
<li><a href="#9-%E5%B0%86%E5%8F%AF%E5%8F%98%E5%BC%95%E7%94%A8%E9%99%8D%E7%BA%A7%E4%B8%BA%E5%85%B1%E4%BA%AB%E5%BC%95%E7%94%A8%E6%98%AF%E5%AE%89%E5%85%A8%E7%9A%84">9) 将可变引用降级为共享引用是安全的</a></li>
<li><a href="#10-%E9%97%AD%E5%8C%85%E9%81%B5%E5%BE%AA%E5%92%8C%E5%87%BD%E6%95%B0%E7%9B%B8%E5%90%8C%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E7%9C%81%E7%95%A5%E8%A7%84%E5%88%99">10) 闭包遵循和函数相同的生命周期省略规则</a></li>
<li><a href="#11-static-%E5%BC%95%E7%94%A8%E6%80%BB%E8%83%BD%E5%BC%BA%E5%88%B6%E8%BD%AC%E6%8D%A2%E4%B8%BA-a-%E5%BC%95%E7%94%A8">11) <code>'static</code> 引用总能强制转换为 <code>'a</code> 引用</a></li>
</ul>
</li>
<li><a href="#%E6%80%BB%E7%BB%93">总结</a></li>
<li><a href="#%E8%AE%A8%E8%AE%BA">讨论</a></li>
<li><a href="#%E5%85%B3%E6%B3%A8">关注</a></li>
</ul>
<h2 id="介绍"><a class="header" href="#介绍">介绍</a></h2>
<p>我曾经有过的所有这些对生命周期的误解，现在有很多初学者也深陷于此。
我用到的术语可能不是标准的，所以下面列了一个表格来解释它们的用意。</p>
<div class="table-wrapper"><table><thead><tr><th>短语</th><th>意为</th></tr></thead><tbody>
<tr><td><code>T</code></td><td>1) 包含了所有可能类型的集合 <em>或</em><br>2) 这个集合中的类型</td></tr>
<tr><td>所有权类型</td><td>不含引用的类型, 例如 <code>i32</code>, <code>String</code>, <code>Vec</code>, 等</td></tr>
<tr><td>1) 借用类型 <em>或</em><br>2) 引用类型</td><td>不考虑可变性的引用类型, 例如 <code>&amp;i32</code>, <code>&amp;mut i32</code>, 等</td></tr>
<tr><td>1) 可变引用 <em>或</em><br>2) 独占引用</td><td>独占的可变引用, 即 <code>&amp;mut T</code></td></tr>
<tr><td>1) 不可变引用 <em>or</em><br>2) 共享引用</td><td>共享的不可变引用, 即 <code>&amp;T</code></td></tr>
</tbody></table>
</div>
<h2 id="误解列表"><a class="header" href="#误解列表">误解列表</a></h2>
<p>简而言之：变量的生命周期指的是这个变量所指的数据可以被编译器静态验证的、在当前内存地址有效期的长度。
我现在会用大约~8000字来详细地解释一下那些容易误解的地方。</p>
<h3 id="1-t-只包含所有权类型"><a class="header" href="#1-t-只包含所有权类型">1) <code>T</code> 只包含所有权类型</a></h3>
<p>这个误解比起说生命周期，它和泛型更相关，但在Rust中泛型和生命周期是紧密联系在一起的，不可只谈其一。</p>
<p>当我刚开始学习Rust的时候，我理解<code>i32</code>，<code>&amp;i32</code>，和<code>&amp;mut i32</code>是不同的类型，也明白泛型变量<code>T</code>代表着所有可能类型的集合。
但尽管这二者分开都懂，当它们结合在一起的时候我却陷入困惑。在我这个Rust初学者的眼中，泛型是这样的运作的：</p>
<div class="table-wrapper"><table><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td><strong>类型变量</strong></td><td><code>T</code></td><td><code>&amp;T</code></td><td><code>&amp;mut T</code></td></tr>
<tr><td><strong>例子</strong></td><td><code>i32</code></td><td><code>&amp;i32</code></td><td><code>&amp;mut i32</code></td></tr>
</tbody></table>
</div>
<p><code>T</code> 包含一切所有权类型； <code>&amp;T</code> 包含一切不可变借用类型； <code>&amp;mut T</code> 包含一切可变借用类型。
<code>T</code>， <code>&amp;T</code>， 和 <code>&amp;mut T</code> 是不相交的有限集。 简洁明了，符合直觉，但却完全错误。
下面这才是泛型真正的运作方式：</p>
<div class="table-wrapper"><table><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td><strong>类型变量</strong></td><td><code>T</code></td><td><code>&amp;T</code></td><td><code>&amp;mut T</code></td></tr>
<tr><td><strong>例子</strong></td><td><code>i32</code>, <code>&amp;i32</code>, <code>&amp;mut i32</code>, <code>&amp;&amp;i32</code>, <code>&amp;mut &amp;mut i32</code>, ...</td><td><code>&amp;i32</code>, <code>&amp;&amp;i32</code>, <code>&amp;&amp;mut i32</code>, ...</td><td><code>&amp;mut i32</code>, <code>&amp;mut &amp;mut i32</code>, <code>&amp;mut &amp;i32</code>, ...</td></tr>
</tbody></table>
</div>
<p><code>T</code>, <code>&amp;T</code>, 和 <code>&amp;mut T</code> 都是无限集, 因为你可以无限借用一个类型。
<code>T</code> 是 <code>&amp;T</code> 和 <code>&amp;mut T</code>的超集. <code>&amp;T</code> 和 <code>&amp;mut T</code> 是不相交的集合。
让我们用几个例子来检验一下这些概念:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>trait Trait {}

impl&lt;T&gt; Trait for T {}

impl&lt;T&gt; Trait for &amp;T {} // 编译错误

impl&lt;T&gt; Trait for &amp;mut T {} // 编译错误
<span class="boring">}
</span></code></pre></pre>
<p>上面的代码并不能如愿编译:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0119]: conflicting implementations of trait `Trait` for type `&amp;_`:
 --&gt; src/lib.rs:5:1
  |
3 | impl&lt;T&gt; Trait for T {}
  | ------------------- first implementation here
4 |
5 | impl&lt;T&gt; Trait for &amp;T {}
  | ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&amp;_`

error[E0119]: conflicting implementations of trait `Trait` for type `&amp;mut _`:
 --&gt; src/lib.rs:7:1
  |
3 | impl&lt;T&gt; Trait for T {}
  | ------------------- first implementation here
...
7 | impl&lt;T&gt; Trait for &amp;mut T {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&amp;mut _`
<span class="boring">}
</span></code></pre></pre>
<p>编译器不允许我们为<code>&amp;T</code>和<code>&amp;mut T</code>实现<code>Trait</code>，因为这样会与为<code>T</code>实现的<code>Trait</code>冲突，
<code>T</code>本身已经包含了所有<code>&amp;T</code>和<code>&amp;mut T</code>。下面的代码能够如愿编译，因为<code>&amp;T</code>和<code>&amp;mut T</code>是不相交的：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>trait Trait {}

impl&lt;T&gt; Trait for &amp;T {} // 编译通过

impl&lt;T&gt; Trait for &amp;mut T {} // 编译通过
<span class="boring">}
</span></code></pre></pre>
<p><strong>要点</strong></p>
<ul>
<li><code>T</code> 是 <code>&amp;T</code> 和 <code>&amp;mut T</code>的超集</li>
<li><code>&amp;T</code> 和 <code>&amp;mut T</code> 是不相交的集合</li>
</ul>
<h3 id="2-如果-t-static-那么-t-必须在整个程序运行中都是有效的"><a class="header" href="#2-如果-t-static-那么-t-必须在整个程序运行中都是有效的">2) 如果 <code>T: 'static</code> 那么 <code>T</code> 必须在整个程序运行中都是有效的</a></h3>
<p><strong>误解推论</strong></p>
<ul>
<li><code>T: 'static</code> 应该被看作 <em>&quot; <code>T</code> 拥有 <code>'static</code> 生命周期 &quot;</em></li>
<li><code>&amp;'static T</code> 和 <code>T: 'static</code> 没有区别</li>
<li>如果 <code>T: 'static</code> 那么 <code>T</code> 必须为不可变的</li>
<li>如果 <code>T: 'static</code> 那么 <code>T</code> 只能在编译期创建</li>
</ul>
<p>大部分Rust初学者是从类似下面这个代码示例中接触到 <code>'static</code> 生命周期的：</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    let str_literal: &amp;'static str = &quot;str literal&quot;;
}
</code></pre></pre>
<p>他们被告知 <code>&quot;str literal&quot;</code> 是硬编码在编译出来的二进制文件中的，
并会在运行时被加载到只读内存，所以必须是不可变的且在整个程序的运行中都是有效的，
这就是它成为 <code>'static</code> 的原因。
而这些观念又进一步被用 <code>static</code> 关键字来定义静态变量的规则所加强。</p>
<pre><pre class="playground"><code class="language-rust">static BYTES: [u8; 3] = [1, 2, 3];
static mut MUT_BYTES: [u8; 3] = [1, 2, 3];

fn main() {
   MUT_BYTES[0] = 99; // 编译错误，修改静态变量是unsafe的

    unsafe {
        MUT_BYTES[0] = 99;
        assert_eq!(99, MUT_BYTES[0]);
    }
}
</code></pre></pre>
<p>认为静态变量</p>
<ul>
<li>只可以在编译期创建</li>
<li>必须是不可变的，修改它们是unsafe的</li>
<li>在整个程序的运行过程中都是有效的</li>
</ul>
<p><code>'static</code> 生命周期大概是以静态变量的默认生命周期命名的，对吧？
那么有理由认为<code>'static</code>生命周期也应该遵守相同的规则，不是吗？</p>
<p>是的，但拥有<code>'static</code>生命周期的类型与<code>'static</code>约束的类型是不同的。
后者能在运行时动态分配，可以安全地、自由地修改，可以被drop，
还可以有任意长度的生命周期。</p>
<p>在这个点，很重要的是要区分 <code>&amp;'static T</code> 和 <code>T: 'static</code>。</p>
<p><code>&amp;'static T</code>是对某个<code>T</code>的不可变引用，这个引用可以被无限期地持有直到程序结束。
这只可能发生在<code>T</code>本身不可变且不会在引用被创建后移动的情况下。
<code>T</code>并不需要在编译期就被创建，因为我们可以在运行时动态生成随机数据，
然后以内存泄漏为代价返回<code>'static</code>引用，例如：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use rand;

// 在运行时生成随机&amp;'static str
fn rand_str_generator() -&gt; &amp;'static str {
    let rand_string = rand::random::&lt;u64&gt;().to_string();
    Box::leak(rand_string.into_boxed_str())
}
<span class="boring">}
</span></code></pre></pre>
<p><code>T: 'static</code> 是指<code>T</code>可以被无限期安全地持有直到程序结束。
<code>T: 'static</code>包括所有<code>&amp;'static T</code>，此外还包括所有的所有权类型，比如<code>String</code>, <code>Vec</code>等。
数据的所有者能够保证数据只要还被持有就不会失效，因此所有者可以无限期安全地持有该数据直到程序结束。
<code>T: 'static</code>应该被看作“<code>T</code>受<code>'static</code>生命周期约束”而非“<code>T</code>有着<code>'static</code>生命周期”。
这段代码能帮我们阐释这些概念：</p>
<pre><pre class="playground"><code class="language-rust">use rand;

fn drop_static&lt;T: 'static&gt;(t: T) {
    std::mem::drop(t);
}

fn main() {
    let mut strings: Vec&lt;String&gt; = Vec::new();
    for _ in 0..10 {
        if rand::random() {
            // 所有字符串都是随机生成的
            // 并且是在运行时动态申请的
            let string = rand::random::&lt;u64&gt;().to_string();
            strings.push(string);
        }
    }

    // 这些字符串都是所有权类型，所以它们满足'static约束
    for mut string in strings {
        // 这些字符串都是可以修改的
        string.push_str(&quot;a mutation&quot;);
        // 这些字符串都是可以被drop的
        drop_static(string); // 编译通过
    }

    // 这些字符串都在程序结束之前失效
    println!(&quot;i am the end of the program&quot;);
}
</code></pre></pre>
<p><strong>要点</strong></p>
<ul>
<li><code>T: 'static</code> 应该被看作 <em>“<code>T</code>受<code>'static</code>生命周期约束”</em></li>
<li>如果 <code>T: 'static</code> 那么<code>T</code>可以是有着<code>'static</code>生命周期的借用类型</li>
<li>由于 <code>T: 'static</code> 包括了所有权类型，这意味着<code>T</code>
<ul>
<li>可以在运行时动态分配</li>
<li>不一定要在整个程序的运行过程中都有效</li>
<li>可以被安全地、自由地修改</li>
<li>可以在运行时被动态drop掉</li>
<li>可以有不同长度的生命周期</li>
</ul>
</li>
</ul>
<h3 id="3-a-t-和-t-a-是相同的"><a class="header" href="#3-a-t-和-t-a-是相同的">3) <code>&amp;'a T</code> 和 <code>T: 'a</code> 是相同的</a></h3>
<p>这个误解是上一个的泛化版本。</p>
<p><code>&amp;'a T</code> 不光要求，同时也隐含着 <code>T: 'a</code>， 因为如果<code>T</code>本身都不能在<code>'a</code>内有效，
那对<code>T</code>的有<code>'a</code>生命周期的引用也不可能是有效的。
例如，Rust编译器从来不会允许创建<code>&amp;'static Ref&lt;'a, T&gt;</code>这个类型，因为如果<code>Ref</code>只在<code>'a</code>内有效，我们不可能弄出一个对它的<code>'static</code>的引用。</p>
<p><code>T: 'a</code>包括了所有<code>&amp;'a T</code>，但反过来不对。</p>
<pre><pre class="playground"><code class="language-rust">// 只接受以'a约束的引用类型
fn t_ref&lt;'a, T: 'a&gt;(t: &amp;'a T) {}

// 接受所有以'a约束的类型
fn t_bound&lt;'a, T: 'a&gt;(t: T) {}

// 包含引用的所有权类型
struct Ref&lt;'a, T: 'a&gt;(&amp;'a T);

fn main() {
    let string = String::from(&quot;string&quot;);

    t_bound(&amp;string); // 编译通过
    t_bound(Ref(&amp;string)); // 编译通过
    t_bound(&amp;Ref(&amp;string)); // 编译通过

    t_ref(&amp;string); // 编译通过
    t_ref(Ref(&amp;string)); // 编译错误, 期待接收一个引用，但收到一个结构体
    t_ref(&amp;Ref(&amp;string)); // 编译通过

    // string变量是以'static约束的，也满足'a约束
    t_bound(string); // 编译通过
}
</code></pre></pre>
<p><strong>要点</strong></p>
<ul>
<li><code>T: 'a</code> 比起 <code>&amp;'a T</code>更泛化也更灵活</li>
<li><code>T: 'a</code> 接受所有权类型、包含引用的所有权类型以及引用</li>
<li><code>&amp;'a T</code> 只接受引用</li>
<li>如果 <code>T: 'static</code> 那么 <code>T: 'a</code>, 因为对于所有<code>'a</code>都有<code>'static</code> &gt;= <code>'a</code></li>
</ul>
<h3 id="4-我的代码没用到泛型也不含生命周期"><a class="header" href="#4-我的代码没用到泛型也不含生命周期">4) 我的代码没用到泛型，也不含生命周期</a></h3>
<p><strong>误解推论</strong></p>
<ul>
<li>避免使用泛型和生命周期是可能的</li>
</ul>
<p>这种安慰性的误解的存在是由于Rust的生命周期省略规则，
这些规则让你能够在函数中省略掉生命周期记号，
因为Rust的借用检查器能根据以下规则将它们推导出来：</p>
<ul>
<li>每个传入的引用都会有一个单独的生命周期</li>
<li>如果只有一个传入的生命周期，那么它将被应用到所有输出的引用上</li>
<li>如果有多个传入的生命周期，但其中一个是<code>&amp;self</code>或者<code>&amp;mut self</code>，那么这个生命周期将会被应用到所有输出的引用上</li>
<li>除此之外的输出的生命周期都必须显示标注出来</li>
</ul>
<p>如果一时间难以想明白这么多东西，那让我们来看一些例子：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// 省略
fn print(s: &amp;str);

// 展开
fn print&lt;'a&gt;(s: &amp;'a str);

// 省略
fn trim(s: &amp;str) -&gt; &amp;str;

// 展开
fn trim&lt;'a&gt;(s: &amp;'a str) -&gt; &amp;'a str;

// 不合法，无法确定输出的生命周期，因为没有输入的
fn get_str() -&gt; &amp;str;

// 显式的写法包括
fn get_str&lt;'a&gt;() -&gt; &amp;'a str; // 泛型版本
fn get_str() -&gt; &amp;'static str; // 'static 版本

// 不合法，无法确定输出的生命周期，因为有多个输入
fn overlap(s: &amp;str, t: &amp;str) -&gt; &amp;str;

// 显式(但仍有部分省略)的写法包括
fn overlap&lt;'a&gt;(s: &amp;'a str, t: &amp;str) -&gt; &amp;'a str; // 输出生命周期不能长于s
fn overlap&lt;'a&gt;(s: &amp;str, t: &amp;'a str) -&gt; &amp;'a str; // 输出生命周期不能长于t
fn overlap&lt;'a&gt;(s: &amp;'a str, t: &amp;'a str) -&gt; &amp;'a str; // 输出生命周期不能长于s和t
fn overlap(s: &amp;str, t: &amp;str) -&gt; &amp;'static str; // 输出生命周期可以长于s和t
fn overlap&lt;'a&gt;(s: &amp;str, t: &amp;str) -&gt; &amp;'a str; // 输入和输出的生命周期无关

// 展开
fn overlap&lt;'a, 'b&gt;(s: &amp;'a str, t: &amp;'b str) -&gt; &amp;'a str;
fn overlap&lt;'a, 'b&gt;(s: &amp;'a str, t: &amp;'b str) -&gt; &amp;'b str;
fn overlap&lt;'a&gt;(s: &amp;'a str, t: &amp;'a str) -&gt; &amp;'a str;
fn overlap&lt;'a, 'b&gt;(s: &amp;'a str, t: &amp;'b str) -&gt; &amp;'static str;
fn overlap&lt;'a, 'b, 'c&gt;(s: &amp;'a str, t: &amp;'b str) -&gt; &amp;'c str;

// 省略
fn compare(&amp;self, s: &amp;str) -&gt; &amp;str;

// 展开
fn compare&lt;'a, 'b&gt;(&amp;'a self, &amp;'b str) -&gt; &amp;'a str;
<span class="boring">}
</span></code></pre></pre>
<p>如果你曾写过</p>
<ul>
<li>结构体方法</li>
<li>接收引用的函数</li>
<li>返回引用的函数</li>
<li>泛型函数</li>
<li>trait object(后面会有更详细的讨论)</li>
<li>闭包(后面会有更详细的讨论)</li>
</ul>
<p>那么你的代码就有被省略的泛型生命周期记号。</p>
<p><strong>要点</strong></p>
<ul>
<li>几乎所有Rust代码都是泛型代码，到处都有被省略的生命周期记号</li>
</ul>
<h3 id="5-如果编译能通过那么我的生命周期标注就是正确的"><a class="header" href="#5-如果编译能通过那么我的生命周期标注就是正确的">5) 如果编译能通过，那么我的生命周期标注就是正确的</a></h3>
<p><strong>误解推论</strong></p>
<ul>
<li>Rust对函数的的生命周期省略规则总是正确的</li>
<li>Rust的借用检查器在技术上和语义上总是正确的</li>
<li>Rust比我更了解我的程序的语义</li>
</ul>
<p>Rust程序是有可能在技术上能通过编译，但语义上仍然是错的。来看一下这个例子：</p>
<pre><pre class="playground"><code class="language-rust">struct ByteIter&lt;'a&gt; {
    remainder: &amp;'a [u8]
}

impl&lt;'a&gt; ByteIter&lt;'a&gt; {
    fn next(&amp;mut self) -&gt; Option&lt;&amp;u8&gt; {
        if self.remainder.is_empty() {
            None
        } else {
            let byte = &amp;self.remainder[0];
            self.remainder = &amp;self.remainder[1..];
            Some(byte)
        }
    }
}

fn main() {
    let mut bytes = ByteIter { remainder: b&quot;1&quot; };
    assert_eq!(Some(&amp;b'1'), bytes.next());
    assert_eq!(None, bytes.next());
}
</code></pre></pre>
<p><code>ByteIter</code> 是在字节切片上迭代的迭代器，为了简洁我们跳过对 <code>Iterator</code> trait的实现。
这看起来没什么问题，但如果我们想同时检查多个字节呢？</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    let mut bytes = ByteIter { remainder: b&quot;1123&quot; };
    let byte_1 = bytes.next();
    let byte_2 = bytes.next();
    if byte_1 == byte_2 {
        // 做点什么
    }
}
</code></pre></pre>
<p>啊哦！编译错误：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0499]: cannot borrow `bytes` as mutable more than once at a time
  --&gt; src/main.rs:20:18
   |
19 |     let byte_1 = bytes.next();
   |                  ----- first mutable borrow occurs here
20 |     let byte_2 = bytes.next();
   |                  ^^^^^ second mutable borrow occurs here
21 |     if byte_1 == byte_2 {
   |        ------ first borrow later used here
<span class="boring">}
</span></code></pre></pre>
<p>我觉得我们可以拷贝每一个字节。拷贝在我们处理字节的时候是可行的，
但当我们从 <code>ByteIter</code> 转向泛型切片迭代器用来迭代任意 <code>&amp;'a [T]</code> 的时候
我们也会想到将来可能它会被应用到那些拷贝/克隆的代价很昂贵或根本不可能的类型上。
噢，我想我们对这没什么办法，代码能过编译，那么生命周期标记必然是对的不是吗？</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>struct ByteIter&lt;'a&gt; {
    remainder: &amp;'a [u8]
}

impl&lt;'a&gt; ByteIter&lt;'a&gt; {
    fn next&lt;'b&gt;(&amp;'b mut self) -&gt; Option&lt;&amp;'b u8&gt; {
        if self.remainder.is_empty() {
            None
        } else {
            let byte = &amp;self.remainder[0];
            self.remainder = &amp;self.remainder[1..];
            Some(byte)
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>这一点帮助都没有，我仍然搞不明白。这里有个只有Rust专家才知道的小窍门：
给你的生命周期标记取个有描述性的名字。我们再试一次：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>struct ByteIter&lt;'remainder&gt; {
    remainder: &amp;'remainder [u8]
}

impl&lt;'remainder&gt; ByteIter&lt;'remainder&gt; {
    fn next&lt;'mut_self&gt;(&amp;'mut_self mut self) -&gt; Option&lt;&amp;'mut_self u8&gt; {
        if self.remainder.is_empty() {
            None
        } else {
            let byte = &amp;self.remainder[0];
            self.remainder = &amp;self.remainder[1..];
            Some(byte)
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>每个返回的字节都被用 <code>'mut_self</code> 标记了，但这些字节显然是来自于 <code>'remainder</code> 的，
让我们来改一下。</p>
<pre><pre class="playground"><code class="language-rust">struct ByteIter&lt;'remainder&gt; {
    remainder: &amp;'remainder [u8]
}

impl&lt;'remainder&gt; ByteIter&lt;'remainder&gt; {
    fn next(&amp;mut self) -&gt; Option&lt;&amp;'remainder u8&gt; {
        if self.remainder.is_empty() {
            None
        } else {
            let byte = &amp;self.remainder[0];
            self.remainder = &amp;self.remainder[1..];
            Some(byte)
        }
    }
}

fn main() {
    let mut bytes = ByteIter { remainder: b&quot;1123&quot; };
    let byte_1 = bytes.next();
    let byte_2 = bytes.next();
    std::mem::drop(bytes); // 我们甚至可以在这里把迭代器drop掉！
    if byte_1 == byte_2 { // 编译通过
        // 做点什么
    }
}
</code></pre></pre>
<p>现在让我们回顾一下，我们前一版的程序显然是错误的，但为什么Rust仍然允许它通过编译呢？
答案很简单：这么做是内存安全的。</p>
<p>Rust的借用检查器对程序的生命周期标记只要求到能够以静态的方式验证程序的内存安全。
Rust会爽快地编译一个程序，即使它的生命周期标记有语义上的错误，
这带来的结果就是程序会变得过于受限。</p>
<p>来看一个与前一个相反的例子：Rust的生命周期省略规则恰好在这个例子上语义是正确的，
但我们却无意中用了一些多余的显式生命周期标记写了个非常受限的方法。</p>
<pre><pre class="playground"><code class="language-rust">#[derive(Debug)]
struct NumRef&lt;'a&gt;(&amp;'a i32);

impl&lt;'a&gt; NumRef&lt;'a&gt; {
    // 我的结构体是在'a上泛型的，所以我同样也要
    // 标记一下我的self参数，对吗？（答案是：不，不对）
    fn some_method(&amp;'a mut self) {}
}

fn main() {
    let mut num_ref = NumRef(&amp;5);
    num_ref.some_method(); // 可变借用num_ref直到它剩余的生命周期结束
    num_ref.some_method(); // 编译错误
    println!(&quot;{:?}&quot;, num_ref); // 同样编译错误
}
</code></pre></pre>
<p>如果我们有一个在 <code>'a</code> 上的泛型，我们几乎永远不会想要写一个接收 <code>&amp;'a mut self</code>的方法。
因为这意味着我们告诉Rust，这个方法会可变借用这个结构体直到整个结构体生命周期结束。
这也就告诉Rust的借用检查器最多只允许 <code>some_method</code> 被调用一次，
在这之后这个结构体将会被永久性地可变借用走，也就变得不可用了。
这样的用例非常非常少，但处于困惑中的初学者非常容易写出这种代码，并能通过编译。
正确的做法是不要添加这些多余的显式生命周期标记，让Rust的生命周期省略规则来处理它：</p>
<pre><pre class="playground"><code class="language-rust">#[derive(Debug)]
struct NumRef&lt;'a&gt;(&amp;'a i32);

impl&lt;'a&gt; NumRef&lt;'a&gt; {
    // 去掉mut self前面的'a
    fn some_method(&amp;mut self) {}

    // 上一段代码脱掉语法糖后变为
    fn some_method_desugared&lt;'b&gt;(&amp;'b mut self){}
}

fn main() {
    let mut num_ref = NumRef(&amp;5);
    num_ref.some_method();
    num_ref.some_method(); // 编译通过
    println!(&quot;{:?}&quot;, num_ref); // 编译通过
}
</code></pre></pre>
<p><strong>要点</strong></p>
<ul>
<li>Rust的函数生命周期省略规则并不总是对所有情况都正确的</li>
<li>Rust对你的程序的语义了解并不比你多</li>
<li>给你的生命周期标记起一个更有描述性的名字</li>
<li>在你使用显式生命周期标记的时候要想清楚它们应该被用在哪以及为什么要这么用</li>
</ul>
<h3 id="6-装箱的trait对象没有生命周期"><a class="header" href="#6-装箱的trait对象没有生命周期">6) 装箱的trait对象没有生命周期</a></h3>
<p>早前我们讨论了Rust对函数的生命周期省略规则。Rust同样有着对于trait对象的生命周期省略规则，它们是：</p>
<ul>
<li>如果一个trait对象作为一个类型参数传递到泛型中，那么它的生命约束会从它包含的类型中推断
<ul>
<li>如果包含的类型中有唯一的约束，那么就使用这个约束。</li>
<li>如果包含的类型中有超过一个约束，那么必须显式指定约束。</li>
</ul>
</li>
<li>如果以上都不适用，那么：
<ul>
<li>如果trait是以单个生命周期约束定义的，那么就使用这个约束</li>
<li>如果所有生命周期约束都是 <code>'static</code> 的，那么就使用 <code>'static</code> 作为约束</li>
<li>如果trait没有生命周期约束，那么它的生命周期将会从表达式中推断，如果不在表达式中，那么就是 <code>'static</code> 的</li>
</ul>
</li>
</ul>
<p>这么多东西听起来超级复杂，但我们可以简单地总结为 <em>&quot;trait对象的生命周期约束是从上下文中推断出来的。&quot;</em> 
在我们看过几个例子后，我们会发现生命周期约束推断其实是很符合直觉的，我们不需要去记这些很正式的规则。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::cell::Ref;

trait Trait {}

// 省略
type T1 = Box&lt;dyn Trait&gt;;
// 展开，Box&lt;T&gt;对T没有生命周期约束，所以被推断为'static
type T2 = Box&lt;dyn Trait + 'static&gt;;

// 省略
impl dyn Trait {}
// 展开
impl dyn Trait + 'static {}

// 省略
type T3&lt;'a&gt; = &amp;'a dyn Trait;
// 展开, 因为&amp;'a T 要求 T: 'a, 所以推断为 'a
type T4&lt;'a&gt; = &amp;'a (dyn Trait + 'a);

// 省略
type T5&lt;'a&gt; = Ref&lt;'a, dyn Trait&gt;;
// 展开, 因为Ref&lt;'a, T&gt; 要求 T: 'a, 所以推断为 'a
type T6&lt;'a&gt; = Ref&lt;'a, dyn Trait + 'a&gt;;

trait GenericTrait&lt;'a&gt;: 'a {}

// 省略
type T7&lt;'a&gt; = Box&lt;dyn GenericTrait&lt;'a&gt;&gt;;
// 展开
type T8&lt;'a&gt; = Box&lt;dyn GenericTrait&lt;'a&gt; + 'a&gt;;

// 省略
impl&lt;'a&gt; dyn GenericTrait&lt;'a&gt; {}
// 展开
impl&lt;'a&gt; dyn GenericTrait&lt;'a&gt; + 'a {}
<span class="boring">}
</span></code></pre></pre>
<p>实现了某个trait的具体的类型可以包含引用，因此它们同样拥有生命周期约束，且对应的trait对象也有生命周期约束。
你也可以直接为引用实现trait，而引用显然有生命周期约束。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>trait Trait {}

struct Struct {}
struct Ref&lt;'a, T&gt;(&amp;'a T);

impl Trait for Struct {}
impl Trait for &amp;Struct {} // 直接在引用类型上实现Trait
impl&lt;'a, T&gt; Trait for Ref&lt;'a, T&gt; {} // 在包含引用的类型上实现Trait
<span class="boring">}
</span></code></pre></pre>
<p>不管怎样，这都值得我们仔细研究，因为新手们经常在将一个使用trait对象的函数重构成使用泛型的函数（或者反过来）的时候感到困惑。
我们来看看这个例子：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::fmt::Display;

fn dynamic_thread_print(t: Box&lt;dyn Display + Send&gt;) {
    std::thread::spawn(move || {
        println!(&quot;{}&quot;, t);
    }).join();
}

fn static_thread_print&lt;T: Display + Send&gt;(t: T) {
    std::thread::spawn(move || {
        println!(&quot;{}&quot;, t);
    }).join();
}
<span class="boring">}
</span></code></pre></pre>
<p>这会抛出下面的编译错误：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0310]: the parameter type `T` may not live long enough
  --&gt; src/lib.rs:10:5
   |
9  | fn static_thread_print&lt;T: Display + Send&gt;(t: T) {
   |                        -- help: consider adding an explicit lifetime bound...: `T: 'static +`
10 |     std::thread::spawn(move || {
   |     ^^^^^^^^^^^^^^^^^^
   |
note: ...so that the type `[closure@src/lib.rs:10:24: 12:6 t:T]` will meet its required lifetime bounds
  --&gt; src/lib.rs:10:5
   |
10 |     std::thread::spawn(move || {
   |     ^^^^^^^^^^^^^^^^^^
<span class="boring">}
</span></code></pre></pre>
<p>很好，编译器告诉了我们怎么解决这个问题，我们来试试。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::fmt::Display;

fn dynamic_thread_print(t: Box&lt;dyn Display + Send&gt;) {
    std::thread::spawn(move || {
        println!(&quot;{}&quot;, t);
    }).join();
}

fn static_thread_print&lt;T: Display + Send + 'static&gt;(t: T) {
    std::thread::spawn(move || {
        println!(&quot;{}&quot;, t);
    }).join();
}
<span class="boring">}
</span></code></pre></pre>
<p>编译通过，但这两个函数放在一块儿看起来有点怪，为什么第二个函数对 <code>T</code> 有 <code>'static</code> 约束，而第一个没有？
这个问题很刁钻。根据生命周期省略规则，Rust自动为第一个函数推断出 <code>'static</code> 约束，所以两个函数实际上都有 <code>'static</code> 约束。
在Rust编译器的眼中是这样的：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::fmt::Display;

fn dynamic_thread_print(t: Box&lt;dyn Display + Send + 'static&gt;) {
    std::thread::spawn(move || {
        println!(&quot;{}&quot;, t);
    }).join();
}

fn static_thread_print&lt;T: Display + Send + 'static&gt;(t: T) {
    std::thread::spawn(move || {
        println!(&quot;{}&quot;, t);
    }).join();
}
<span class="boring">}
</span></code></pre></pre>
<p><strong>要点</strong></p>
<ul>
<li>所有trait对象都有着默认推断的生命周期约束</li>
</ul>
<h3 id="7-编译器报错信息会告诉我怎么修改我的代码"><a class="header" href="#7-编译器报错信息会告诉我怎么修改我的代码">7) 编译器报错信息会告诉我怎么修改我的代码</a></h3>
<p><strong>误解推论</strong></p>
<ul>
<li>Rust编译器对于trait objects的生命周期省略规则总是对的</li>
<li>Rust编译器比我更懂我代码的语义</li>
</ul>
<p>这个误解是前两个误解的合二为一的例子：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::fmt::Display;

fn box_displayable&lt;T: Display&gt;(t: T) -&gt; Box&lt;dyn Display&gt; {
    Box::new(t)
}
<span class="boring">}
</span></code></pre></pre>
<p>抛出如下错误：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0310]: the parameter type `T` may not live long enough
 --&gt; src/lib.rs:4:5
  |
3 | fn box_displayable&lt;T: Display&gt;(t: T) -&gt; Box&lt;dyn Display&gt; {
  |                    -- help: consider adding an explicit lifetime bound...: `T: 'static +`
4 |     Box::new(t)
  |     ^^^^^^^^^^^
  |
note: ...so that the type `T` will meet its required lifetime bounds
 --&gt; src/lib.rs:4:5
  |
4 |     Box::new(t)
  |     ^^^^^^^^^^^
<span class="boring">}
</span></code></pre></pre>
<p>好吧，让我们照着编译器告诉我们的方式修改它，别在意这种改法基于了一个没有告知的事实：
编译器自动为我们的boxed trait object推断了一个<code>'static</code>的生命周期约束。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::fmt::Display;

fn box_displayable&lt;T: Display + 'static&gt;(t: T) -&gt; Box&lt;dyn Display&gt; {
    Box::new(t)
}
<span class="boring">}
</span></code></pre></pre>
<p>现在编译通过了，但这真的是我们想要的吗？可能是，也可能不是，编译去并没有告诉我们其它解决方法
但这个也许合适。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::fmt::Display;

fn box_displayable&lt;'a, T: Display + 'a&gt;(t: T) -&gt; Box&lt;dyn Display + 'a&gt; {
    Box::new(t)
}
<span class="boring">}
</span></code></pre></pre>
<p>这个函数接收的参数和前一个版本一样，但多了不少东西。这样写能让它更好吗？不一定，
这取决于我们的程序的要求和约束。这个例子有些抽象，让我们来看看更简单明了的情况。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn return_first(a: &amp;str, b: &amp;str) -&gt; &amp;str {
    a
}
<span class="boring">}
</span></code></pre></pre>
<p>报错:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0106]: missing lifetime specifier
 --&gt; src/lib.rs:1:38
  |
1 | fn return_first(a: &amp;str, b: &amp;str) -&gt; &amp;str {
  |                    ----     ----     ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
help: consider introducing a named lifetime parameter
  |
1 | fn return_first&lt;'a&gt;(a: &amp;'a str, b: &amp;'a str) -&gt; &amp;'a str {
  |                ^^^^    ^^^^^^^     ^^^^^^^     ^^^
<span class="boring">}
</span></code></pre></pre>
<p>这个错误信息建议我们给输入和输出打上相同的生命周期标记。
这么做虽然能使得编译通过，但却过度限制了返回类型。
我们真正想要的是这个：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn return_first&lt;'a&gt;(a: &amp;'a str, b: &amp;str) -&gt; &amp;'a str {
    a
}
<span class="boring">}
</span></code></pre></pre>
<p><strong>要点</strong></p>
<ul>
<li>Rust对trait object的生命周期省略规则并不是在所有情况下都正确。</li>
<li>Rust不见得比你更懂你代码的语义。</li>
<li>Rust编译错误信息给出的修改建议可能能让你的代码编译通过，但这不一定是最符合你的要求的。</li>
</ul>
<h3 id="8-生命周期可以在运行时变长缩短"><a class="header" href="#8-生命周期可以在运行时变长缩短">8) 生命周期可以在运行时变长缩短</a></h3>
<p><strong>误解推论</strong></p>
<ul>
<li>容器类型可以通过更换引用在运行时更改自己的生命周期</li>
<li>Rust的借用检查会进行深入的控制流分析</li>
</ul>
<p>这过不了编译：</p>
<pre><pre class="playground"><code class="language-rust">struct Has&lt;'lifetime&gt; {
    lifetime: &amp;'lifetime str,
}

fn main() {
    let long = String::from(&quot;long&quot;);
    let mut has = Has { lifetime: &amp;long };
    assert_eq!(has.lifetime, &quot;long&quot;);

    {
        let short = String::from(&quot;short&quot;);
        // 换成短生命周期
        has.lifetime = &amp;short;
        assert_eq!(has.lifetime, &quot;short&quot;);

        // 换回长生命周期（并不行）
        has.lifetime = &amp;long;
        assert_eq!(has.lifetime, &quot;long&quot;);
        // `short`在这里析构
    }

    // 编译错误，`short`在析构后仍处于借用状态
    assert_eq!(has.lifetime, &quot;long&quot;);
}
</code></pre></pre>
<p>报错：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0597]: `short` does not live long enough
  --&gt; src/main.rs:11:24
   |
11 |         has.lifetime = &amp;short;
   |                        ^^^^^^ borrowed value does not live long enough
...
15 |     }
   |     - `short` dropped here while still borrowed
16 |     assert_eq!(has.lifetime, &quot;long&quot;);
   |     --------------------------------- borrow later used here
<span class="boring">}
</span></code></pre></pre>
<p>下面这个代码同样过不了编译，报的错和上面一样。</p>
<pre><pre class="playground"><code class="language-rust">struct Has&lt;'lifetime&gt; {
    lifetime: &amp;'lifetime str,
}

fn main() {
    let long = String::from(&quot;long&quot;);
    let mut has = Has { lifetime: &amp;long };
    assert_eq!(has.lifetime, &quot;long&quot;);

    // 这个代码块不会被执行
    if false {
        let short = String::from(&quot;short&quot;);
        // 换成短生命周期
        has.lifetime = &amp;short;
        assert_eq!(has.lifetime, &quot;short&quot;);

        // 换回长生命周期（并不行）
        has.lifetime = &amp;long;
        assert_eq!(has.lifetime, &quot;long&quot;);
        // `short`在这里析构
    }

    // 仍旧编译错误，`short`在析构后仍处于借用状态
    assert_eq!(has.lifetime, &quot;long&quot;);
}
</code></pre></pre>
<p>生命周期只会在编译期被静态验证，并且Rust的借用检查只能做到基本的控制流分析，
它假设每个<code>if-else</code>中的代码块和<code>match</code>的每个分支都会被执行，
并且其中的每一个变量都能被指定一个最短的生命周期。
一旦变量被指定了一个生命周期，它就一直受到这个生命周期约束。变量的生命周期只能缩短，
并且所有缩短都会在编译器被确定。</p>
<p><strong>要点</strong></p>
<ul>
<li>生命周期是在编译期静态验证的</li>
<li>生命周期不能在运行时变长、缩短或者改变</li>
<li>Rust的借用检查总是会为所有变量指定一个最短可能的生命周期，并且假定所有代码路径都会被执行</li>
</ul>
<h3 id="9-将可变引用降级为共享引用是安全的"><a class="header" href="#9-将可变引用降级为共享引用是安全的">9) 将可变引用降级为共享引用是安全的</a></h3>
<p><strong>误解推论</strong></p>
<ul>
<li>重新借用一个引用会终止它的生命周期并且开始一个新的</li>
</ul>
<p>你可以向一个接收共享引用的函数传递一个可变引用，因为Rust会隐式将可变引用重新借用为不可变引用：</p>
<pre><pre class="playground"><code class="language-rust">fn takes_shared_ref(n: &amp;i32) {}

fn main() {
    let mut a = 10;
    takes_shared_ref(&amp;mut a); // 编译通过
    takes_shared_ref(&amp;*(&amp;mut a)); // 上一行的显式写法
}
</code></pre></pre>
<p>直觉上这没问题，将一个可变引用重新借用为不可变引用，应该不会有什么害处不是吗？
然而并非如此，下面的代码过不了编译。</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    let mut a = 10;
    let b: &amp;i32 = &amp;*(&amp;mut a); // 重新借用为不可变
    let c: &amp;i32 = &amp;a;
    dbg!(b, c); // 编译错误
}
</code></pre></pre>
<p>报错：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
 --&gt; src/main.rs:4:19
  |
3 |     let b: &amp;i32 = &amp;*(&amp;mut a);
  |                     -------- mutable borrow occurs here
4 |     let c: &amp;i32 = &amp;a;
  |                   ^^ immutable borrow occurs here
5 |     dbg!(b, c);
  |          - mutable borrow later used here
<span class="boring">}
</span></code></pre></pre>
<p>可变借用出现后立即重新借用为不可变引用，然后可变引用自身析构。
为什么Rust会认为这个不可变的重新借用仍具有可变引用的独占生命周期？
虽然上面这个例子没什么问题，但允许将可变引用降级为共享引用实际上引入了潜在的内存安全问题。</p>
<pre><pre class="playground"><code class="language-rust">use std::sync::Mutex;

struct Struct {
    mutex: Mutex&lt;String&gt;
}

impl Struct {
    // 将self的可变引用降级为str的共享引用
    fn get_string(&amp;mut self) -&gt; &amp;str {
        self.mutex.get_mut().unwrap()
    }
    fn mutate_string(&amp;self) {
        // 如果Rust允许将可变引用降级为共享引用，
        // 那么下面这行代码会使得所有从get_string中得到的共享引用失效
        *self.mutex.lock().unwrap() = &quot;surprise!&quot;.to_owned();
    }
}

fn main() {
    let mut s = Struct {
        mutex: Mutex::new(&quot;string&quot;.to_owned())
    };
    let str_ref = s.get_string(); // 可变引用降级为共享引用
    s.mutate_string(); // str_ref失效，变为悬空指针
    dbg!(str_ref); // 编译错误，和我们预期的一样
}
</code></pre></pre>
<p>这里的问题在于，当你将一个可变引用重新借用为共享引用，你会遇到一点麻烦：
即使可变引用已经析构，重新借用出来的共享引用还是会将可变引用的生命周期延长到和自己一样长。
这种重新借用出来的共享引用非常难用，因为它不能与其它共享引用共存。
它有着可变引用和不可变引用的所有缺点，却没有它们各自的优点。
我认为将可变引用重新借用为共享引用应该被认为是Rust的反模式（anti-pattern)。
对这种反模式保持警惕很重要，这可以让你在看到下面这样的代码的时候更容易发现它：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// 将T的可变引用降级为共享引用
fn some_function&lt;T&gt;(some_arg: &amp;mut T) -&gt; &amp;T;

struct Struct;

impl Struct {
    // 将self的可变引用降级为self共享引用
    fn some_method(&amp;mut self) -&gt; &amp;self;

    // 将self的可变引用降级为T的共享引用
    fn other_method(&amp;mut self) -&gt; &amp;T;
}
<span class="boring">}
</span></code></pre></pre>
<p>即使你避免了函数和方法签名中的重新借用，Rust仍然会自动隐式重新借用，
所以很容易无意中遇到这样的问题：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::collections::HashMap;

type PlayerID = i32;

#[derive(Debug, Default)]
struct Player {
    score: i32,
}

fn start_game(player_a: PlayerID, player_b: PlayerID, server: &amp;mut HashMap&lt;PlayerID, Player&gt;) {
    // 从服务器中获取player，如果不存在则创建并插入一个新的
    let player_a: &amp;Player = server.entry(player_a).or_default();
    let player_b: &amp;Player = server.entry(player_b).or_default();

    // 用player做点什么
    dbg!(player_a, player_b); // 编译错误
}
<span class="boring">}
</span></code></pre></pre>
<p>上面的代码编译失败。因为 <code>or_default()</code> 返回一个 <code>&amp;mut Player</code>，
而我们的显式类型标注 <code>&amp;Player</code> 使得这个 <code>&amp;mut Player</code> 被隐式重新借用为 <code>&amp;Player</code> 。
为了通过编译，我们不得不这样写：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::collections::HashMap;

type PlayerID = i32;

#[derive(Debug, Default)]
struct Player {
    score: i32,
}

fn start_game(player_a: PlayerID, player_b: PlayerID, server: &amp;mut HashMap&lt;PlayerID, Player&gt;) {
    // 因为我们不能把它们放在一起用，所以这里把返回的Player可变引用析构掉
    server.entry(player_a).or_default();
    server.entry(player_b).or_default();

    // 再次获取这些Player，这次以不可变的方式，避免出现隐式重新借用
    let player_a = server.get(&amp;player_a);
    let player_b = server.get(&amp;player_b);

    // 用Player做点什么
    dbg!(player_a, player_b); // 编译通过
}
<span class="boring">}
</span></code></pre></pre>
<p>虽然有点尴尬和笨重，但这也算是为内存安全做出的牺牲。</p>
<p><strong>要点</strong></p>
<ul>
<li>尽量不要把可变引用重新借用为共享引用，不然你会遇到不少麻烦</li>
<li>重新借用一个可变引用不会使得它的生命周期终结，即使这个可变引用已经析构</li>
</ul>
<h3 id="10-闭包遵循和函数相同的生命周期省略规则"><a class="header" href="#10-闭包遵循和函数相同的生命周期省略规则">10) 闭包遵循和函数相同的生命周期省略规则</a></h3>
<p>比起误解，这更像是Rust的一个小陷阱。</p>
<p>闭包，虽然也是个函数，但是它并不遵循和函数相同的生命周期省略规则。</p>
<pre><pre class="playground"><code class="language-rust">fn function(x: &amp;i32) -&gt; &amp;i32 {
    x
}

fn main() {
    let closure = |x: &amp;i32| x;
}
</code></pre></pre>
<p>报错：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error: lifetime may not live long enough
 --&gt; src/main.rs:6:29
  |
6 |     let closure = |x: &amp;i32| x;
  |                       -   - ^ returning this value requires that `'1` must outlive `'2`
  |                       |   |
  |                       |   return type of closure is &amp;'2 i32
  |                       let's call the lifetime of this reference `'1`
<span class="boring">}
</span></code></pre></pre>
<p>去掉语法糖后：</p>
<pre><pre class="playground"><code class="language-rust">// 输入的生命周期应用到输出上
fn function&lt;'a&gt;(x: &amp;'a i32) -&gt; &amp;'a i32 {
    x
}

fn main() {
    // 输入和输出有它们自己独有的生命周期
    let closure = for&lt;'a, 'b&gt; |x: &amp;'a i32| -&gt; &amp;'b i32 { x };
    // 注意：上面这行代码不是合法的语法，但可以表达出我们的意思
}
</code></pre></pre>
<p>出现这种差异并没有一个好的理由。闭包最早的实现用的类型推断语义和函数不同，
现在变得没法改了，因为将它们统一起来会造成一个不兼容的改动。
那么我们要怎么样显式标注闭包的类型呢？我们可选的办法有：</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    // 转成trait object，变成不定长类型，编译错误
    let identity: dyn Fn(&amp;i32) -&gt; &amp;i32 = |x: &amp;i32| x;

    // 可以通过将它分配在堆上来绕过这个错误，但这样很笨重
    let identity: Box&lt;dyn Fn(&amp;i32) -&gt; &amp;i32&gt; = Box::new(|x: &amp;i32| x);

    // 也可以跳过分配，直接创建一个静态的引用
    let identity: &amp;dyn Fn(&amp;i32) -&gt; &amp;i32 = &amp;|x: &amp;i32| x;

    // 上一行去掉语法糖之后:)
    let identity: &amp;'static (dyn for&lt;'a&gt; Fn(&amp;'a i32) -&gt; &amp;'a i32 + 'static) = &amp;|x: &amp;i32| -&gt; &amp;i32 { x };

    // 理想中的写法是这样的，但这不是有效的语法
    let identity: impl Fn(&amp;i32) -&gt; &amp;i32 = |x: &amp;i32| x;

    // 这样也不错，但也不是有效的语法
    let identity = for&lt;'a&gt; |x: &amp;'a i32| -&gt; &amp;'a i32 { x };

    // &quot;impl trait&quot;可以写在函数返回的位置，我们也可以这样写
    fn return_identity() -&gt; impl Fn(&amp;i32) -&gt; &amp;i32 {
        |x| x
    }
    let identity = return_identity();

    // 前一种解决方案更泛化的写法
    fn annotate&lt;T, F&gt;(f: F) -&gt; F where F: Fn(&amp;T) -&gt; &amp;T {
        f
    }
    let identity = annotate(|x: &amp;i32| x);
}
</code></pre></pre>
<p>相信你已经注意到，在上面的例子中，当闭包类型使用trait约束的时候会遵循一般函数的生命周期省略规则。</p>
<p>这里没有什么真正的教训和洞察，只是它就是这样的而已。</p>
<p><strong>要点</strong></p>
<ul>
<li>每一门语言都有自己的小陷阱 🤷</li>
</ul>
<h3 id="11-static-引用总能强制转换为-a-引用"><a class="header" href="#11-static-引用总能强制转换为-a-引用">11) <code>'static</code> 引用总能强制转换为 <code>'a</code> 引用</a></h3>
<p>我前面给出了这个例子：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn get_str&lt;'a&gt;() -&gt; &amp;'a str; // 泛型版本
fn get_str() -&gt; &amp;'static str; // 'static版本
<span class="boring">}
</span></code></pre></pre>
<p>有的读者问我这两个在实践中是否有区别。一开始我也不太确定，但不幸的是，
在经过一段时间的研究之后我发现它们在实践中确实存在着区别。</p>
<p>所以一般来说，在操作值得时候我们可以使用 <code>'static</code> 引用来替换 <code>'a</code> 引用，
因为Rust会自动将 <code>'static</code> 引用强制转换到 <code>'a</code> 引用。
直觉上来讲，这没毛病，在一个要求较短生命周期引用的地方使用一个有着更长的生命周期的引用不会造成内存安全问题。
下面的代码和我们想的一样编译通过：</p>
<pre><pre class="playground"><code class="language-rust">use rand;

fn generic_str_fn&lt;'a&gt;() -&gt; &amp;'a str {
    &quot;str&quot;
}

fn static_str_fn() -&gt; &amp;'static str {
    &quot;str&quot;
}

fn a_or_b&lt;T&gt;(a: T, b: T) -&gt; T {
    if rand::random() {
        a
    } else {
        b
    }
}

fn main() {
    let some_string = &quot;string&quot;.to_owned();
    let some_str = &amp;some_string[..];
    let str_ref = a_or_b(some_str, generic_str_fn()); // 编译通过
    let str_ref = a_or_b(some_str, static_str_fn()); // 编译通过
}
</code></pre></pre>
<p>然而，这种强制转换并不会在引用作为函数的类型签名的一部分的时候出现，所以下面这段代码无法通过编译：</p>
<pre><pre class="playground"><code class="language-rust">use rand;

fn generic_str_fn&lt;'a&gt;() -&gt; &amp;'a str {
    &quot;str&quot;
}

fn static_str_fn() -&gt; &amp;'static str {
    &quot;str&quot;
}

fn a_or_b_fn&lt;T, F&gt;(a: T, b_fn: F) -&gt; T
    where F: Fn() -&gt; T
{
    if rand::random() {
        a
    } else {
        b_fn()
    }
}

fn main() {
    let some_string = &quot;string&quot;.to_owned();
    let some_str = &amp;some_string[..];
    let str_ref = a_or_b_fn(some_str, generic_str_fn); // 编译通过
    let str_ref = a_or_b_fn(some_str, static_str_fn); // 编译错误
}
</code></pre></pre>
<p>报错：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0597]: `some_string` does not live long enough
  --&gt; src/main.rs:23:21
   |
23 |     let some_str = &amp;some_string[..];
   |                     ^^^^^^^^^^^ borrowed value does not live long enough
...
25 |     let str_ref = a_or_b_fn(some_str, static_str_fn);
   |                   ---------------------------------- argument requires that `some_string` is borrowed for `'static`
26 | }
   | - `some_string` dropped here while still borrowed
<span class="boring">}
</span></code></pre></pre>
<p>这算不算Rust的小陷阱还有待商榷，因为这不是 <code>&amp;'static str</code> 到 <code>&amp;'a str</code> 简单直接的强制转换，
而是 <code>for&lt;T&gt; Fn() -&gt; &amp;'static T</code> 到 <code>for&lt;'a, T&gt; Fn() -&gt; &amp;'a T</code> 这种更复杂的情况。
前者是值之间的强制转换，后者是类型之间的强制转换。</p>
<p><strong>要点</strong></p>
<ul>
<li>签名为 <code>for&lt;'a, T&gt; Fn() -&gt; &amp;'a T</code> 的函数要比签名为 <code>for&lt;T&gt; fn() -&gt; &amp;'static T</code> 的函数更为灵活，并且能用在更多场景下</li>
</ul>
<h2 id="总结"><a class="header" href="#总结">总结</a></h2>
<ul>
<li><code>T</code> 是 <code>&amp;T</code> 和 <code>&amp;mut T</code> 的超集</li>
<li><code>&amp;T</code> 和 <code>&amp;mut T</code> 是不相交的集合</li>
<li><code>T: 'static</code> 应该被读作 <em>&quot;<code>T</code> 受 <code>'static</code> 生命周期约束&quot;</em></li>
<li>如果 <code>T: 'static</code> 那么 <code>T</code> 可以是一个有着 <code>'static</code> 生命周期的借用类型，或是一个所有权类型</li>
<li>既然 <code>T: 'static</code> 包含了所有权类型，那么意味着 <code>T</code>
<ul>
<li>可以在运行时动态分配</li>
<li>不必在整个程序中都是有效的</li>
<li>可以被安全地任意修改</li>
<li>可以在运行时动态析构</li>
<li>可以有不同长度的生命周期</li>
</ul>
</li>
<li><code>T: 'a</code> 比 <code>&amp;'a T</code> 更泛化、灵活</li>
<li><code>T: 'a</code> 接收所有权类型、带引用的所有权类型，以及引用</li>
<li><code>&amp;'a T</code> 只接收引用</li>
<li>如果 <code>T: 'static</code> 那么 <code>T: 'a</code>，因为对于所有 <code>'a</code> 都有 <code>'static</code> &gt;= <code>'a</code></li>
<li>几乎所有Rust代码都是泛型的，到处都有省略的生命周期</li>
<li>Rust的生命周期省略规则并不是在任何情况下都对</li>
<li>Rust并不比你更了解你程序的语义</li>
<li>给生命周期标记起一个有描述性的名字</li>
<li>考虑清楚哪里需要显式写出生命周期标记，以及为什么要这么写</li>
<li>所有trait object都有默认推断的生命周期约束</li>
<li>Rust的编译错误信息可以让你的代码通过编译，但不一定是最符合你代码要求的</li>
<li>生命周期是在编译期静态验证的</li>
<li>生命周期不会以任何方式在运行时变长缩短</li>
<li>Rust的借用检查总会为每个变量选择一个最短可能的生命周期，并且假定每条代码路径都会被执行</li>
<li>尽量避免将可变引用重新借用为不可变引用，不然你会遇到不少麻烦</li>
<li>重新借用一个可变引用不会终止它的生命周期，即使这个可变引用已经析构</li>
<li>每个语言都有自己的小陷阱 🤷</li>
</ul>
<h2 id="讨论"><a class="header" href="#讨论">讨论</a></h2>
<p>在这些地方讨论这篇文章</p>
<ul>
<li><a href="https://www.reddit.com/r/learnrust/comments/gmrcrq/common_rust_lifetime_misconceptions/">learnrust subreddit</a></li>
<li><a href="https://users.rust-lang.org/t/blog-post-common-rust-lifetime-misconceptions/42950">official Rust users forum</a></li>
<li><a href="https://twitter.com/pretzelhammer/status/1263505856903163910">Twitter</a></li>
<li><a href="https://www.reddit.com/r/rust/comments/golrsx/common_rust_lifetime_misconceptions/">rust subreddit</a></li>
<li><a href="https://news.ycombinator.com/item?id=23279731">Hackernews</a></li>
</ul>
<h2 id="关注"><a class="header" href="#关注">关注</a></h2>
<p><a href="https://twitter.com/pretzelhammer">在Twitter上关注pretzelhammer</a> 来获取最新的博客的更新!</p>
<h2 id="拓展阅读"><a class="header" href="#拓展阅读">拓展阅读</a></h2>
<p><a href="./learning-rust-in-2020.html">Learning Rust in 2020</a></p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../../02_生命周期/Common_Rust_Lifetime_Misconceptions/Common_Rust_Lifetime_Misconceptions.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next" href="../../empty.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../../02_生命周期/Common_Rust_Lifetime_Misconceptions/Common_Rust_Lifetime_Misconceptions.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next" href="../../empty.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>




        <script type="text/javascript">
            window.playground_copyable = true;
        </script>


        <script src="../../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../searcher.js" type="text/javascript" charset="utf-8"></script>

        <script src="../../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->


    </body>
</html>
